#include "LensflareConfig.h"

#include <iomanip>
#include <algorithm>
#include <cctype>
#include <fstream>

using namespace Lensflare;

Config::Config() {
    Init("config.ini");
}

Config::Config(const std::string& fileName) {
    Init(fileName);
}

Config::~Config() {

}

void Config::Init(const std::string& fileName) {
    this->fileName = fileName;
	this->globalCommentIndicator = ";";
	this->globalAssignIndicator = "=";

    std::ifstream inputFileStream;
    inputFileStream.open(this->fileName.c_str(), std::fstream::in);

    if (inputFileStream.is_open()) {
        while (!inputFileStream.eof()) {
            std::string textLine;
            getline(inputFileStream, textLine);

			const unsigned commentIndicator1 = textLine.find(';');
			const unsigned commentIndicator2 = textLine.find('#');
			std::string textWithoutComment;
			std::string comment;
			if(commentIndicator1 != std::string::npos) {
				textWithoutComment = textLine.substr(0, commentIndicator1);
				comment = textLine.substr(commentIndicator1 + 1);
				globalCommentIndicator = textLine[commentIndicator1];
			} else if(commentIndicator2 != std::string::npos) {
				textWithoutComment = textLine.substr(0, commentIndicator2);
				comment = textLine.substr(commentIndicator2 + 1);
				globalCommentIndicator = textLine[commentIndicator2];
			} else {
				textWithoutComment = textLine;
			}

			Trim(textWithoutComment);
			if(textWithoutComment.size() <= 0) {
				//Empty line recognized (can be comment, too)
				lines.push_back(Line(Line::NONE, "", "", comment));
			} else if(textWithoutComment.size() > 2 && textWithoutComment[0] == '[' && textWithoutComment[textWithoutComment.length()-1] == ']') {
				//Section line recognized
				std::string sectionName = textWithoutComment.substr(1, textWithoutComment.length()-2);
				lines.push_back(Line(Line::SECTION, sectionName, "", comment));
			} else {
				const unsigned assignIndicatorIndex = textWithoutComment.find(globalAssignIndicator);
				std::string key;
				std::string value;
				if (assignIndicatorIndex != std::string::npos) {
					key = textWithoutComment.substr(0, assignIndicatorIndex);
					Trim(key);

					if (textWithoutComment.size() > assignIndicatorIndex + 1) {
						value = textWithoutComment.substr(assignIndicatorIndex + 1);
						Trim(value);
					}
				}

				if (!key.empty()) {
					//Property line recognized
					lines.push_back(Line(Line::PROPERTY, key, value, comment));
				} else {
					//Invalid line recognized. Treat as comment.
					lines.push_back(Line(Line::NONE, "", "", textWithoutComment));
				}
			}
        }

        inputFileStream.close();
    }
}

void Config::Save() {
    std::ofstream outputFileStream;
    outputFileStream.open(fileName.c_str());
    if (outputFileStream.is_open()) {
        std::list<Line>::const_iterator it = lines.begin();
		bool firstLine = true;
        while (it != lines.end()) {
            const Line& line = *it;
			std::string commentText;
			if(!line.comment.empty()) {
				commentText.append(globalCommentIndicator);
				//commentText.append(" ");
				commentText.append(line.comment);
			}

			if(!firstLine) {
				outputFileStream << std::endl;
			}

			if(line.lineType == Line::NONE) {
				outputFileStream << commentText;
			} else if(line.lineType == Line::PROPERTY) {
				std::string commentSpace = commentText.empty() ? "" : " ";
				outputFileStream << line.key << " " << globalAssignIndicator << " " << line.value << commentSpace << commentText;
			} else if(line.lineType == Line::SECTION) {
				std::string commentSpace = commentText.empty() ? "" : " ";
				outputFileStream << "[" << line.key << "]" << commentSpace << commentText;
			}

			firstLine = false;
            ++it;
        }
        outputFileStream.flush();
        outputFileStream.close();
    }
}

bool Config::KeyExists(const std::string& section, const std::string& key) {
	if(!key.empty()) {
		std::string currentSection = "";
		std::list<Line>::const_iterator it = lines.begin();
		while (it != lines.end()) {
			const Line& line = *it;
			if(line.lineType == Line::SECTION) {
				currentSection = line.key;
			} else if (line.lineType == Line::PROPERTY && section == currentSection && line.key == key) {
				return true;
			}
			++it;
		}
	}
	return false;
}

bool Config::KeyExists(const std::string& key) {
	return KeyExists("", key);
}

void Config::LeftTrim(std::string& s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
}

void Config::RightTrim(std::string& s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

void Config::Trim(std::string& s) {
    RightTrim(s);
    LeftTrim(s);
}
